#include "login.h"
#include "ui_login.h"
#include "clientwindow.h"
#include "serverwindow.h"
#include "jsonparse.h"
#include "dboperation.h"
#include "taskqueue.h"
#include "clientsubthread.h"

#include <QDesktopWidget>
#include <QMessageBox>

Login::Login(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Login)
{
    ui->setupUi(this);

    // 默认选中的角色是客户端
    ui->radioClient->setChecked(true);
    initWindow(LoginWindow);    // 初始化窗口大小

    // 处理设置窗口中发出的信号
    connect(ui->setPage, &ConfigSet::goback, this, [=]()
    {
        // 栈窗口切换到登录窗口
        // ui->stackedWidget->setCurrentIndex(0);
        ui->stackedWidget->setCurrentWidget(ui->loginPage);
        initWindow(LoginWindow);
    });

    // 加载登录信息
    // 按钮组
    m_btnGrp.addButton(ui->radioClient, 1);
    m_btnGrp.addButton(ui->radioServer, 2);
    void (QButtonGroup::*clicked)(int) = &QButtonGroup::buttonClicked;
    connect(&m_btnGrp, clicked, this, &Login::initLoginInfo);
    // 填充配置文件中的ID和密码
    initLoginInfo(1);
}

Login::~Login()
{
    delete ui;
}

void Login::initWindow(Login::Window type)
{
    // 窗口标题
    // QList<QString>
    QStringList nameList;
    nameList << "用户登录" << "客户端配置" << "服务器配置";

    // 窗口的大小
    QSize size[] =
    {
        QSize(280, 240),
        QSize(300, 350),
        QSize(400, 480)
    };

    // 根据参数对窗口进行属性的设置
    setWindowTitle(nameList[type]);
    setFixedSize(size[type]);
}

void Login::initClientInfo()
{
    // 创建客户端子线程对象, 并启动子线程
    ClientSubThread* subThread = new ClientSubThread;
    subThread->start();
    // 当前窗口被析构, 终止子线程, 析构子线程对象
    connect(qApp, &QApplication::destroyed, this, [=]()
    {
        // 终止线程内部的事件循环, 当前的任务处理完毕, 退出子线程
        // 如果子线程被阻塞(条件变量 / 信号量)是不能退出事件循环的
        // 如果能保证线程退出呢? 在调用这个操作的时候, 让线程不阻塞
        subThread->quit();
        subThread->wait();  // 阻塞等待当前线程任务执行完毕
        subThread->deleteLater();   // delete subThread;
    });

    // 窗口客户端操作对象
    m_cliop = new ClientOperation;
    connect(this, &Login::loginInfo, m_cliop, &ClientOperation::saveLoginInfo);
    // 接收ClientOperation发送的登录状态信号
    connect(m_cliop, &ClientOperation::loginSuccess, this, [=]()
    {
        // 显示客户端主界面
        ClientWindow* cli = new ClientWindow;
        cli->show();
        bool savePwd = ui->savePasswd->isChecked();
        if(savePwd)
        {
            // 获取用户输入数据
            QString userName = ui->netID->text();
            QString passwd = ui->netPasswd->text();
            bool cliRole = ui->radioClient->isChecked();
            JsonParse::Type type = cliRole ? JsonParse::Client : JsonParse::Server;
            JsonParse::getInstance()->writeLoginInfo(userName, passwd, type);
        }
        // 隐藏或者关闭登录窗口
        accept();   // 隐藏模态对话框, 解除阻塞
    });
    connect(m_cliop, &ClientOperation::loginFail, this, [=]()
    {
        QMessageBox::warning(this, "登录失败", "您输入的ID或密码错误!!!");
    });
}

void Login::initLoginInfo(int index)
{
    JsonParse::Type t = index == 1 ? JsonParse::Client : JsonParse::Server;
    LoginInfo info = JsonParse::getInstance()->readLoginInfo(t);
    ui->netID->setText(info.id);
    ui->netPasswd->setText(info.pwd);
}

void Login::resizeEvent(QResizeEvent *)
{
    // 重新让窗口在屏幕里边居中显示
    // 需要知道窗口和屏幕大小, 计算坐标通过 move() 函数进行移动
    // 得到屏幕大小
    QDesktopWidget* desk = QApplication::desktop();
    // 计算窗口左上角的坐标位置
    int x = (desk->width() - this->width()) / 2;
    int y = (desk->height() - this->height()) / 2;
    // 移动窗口
    this->move(x, y);

}

// 点击设置按钮, 自动生成的槽函数会被调用
// 通过转到槽的方式添加槽函数, 就不需要做connect操作了
void Login::on_setBtn_clicked()
{
    // 栈窗口切换到设置界面
    ui->stackedWidget->setCurrentWidget(ui->setPage);

    // 判断客户端角色是否被选中了
    bool bl = ui->radioClient->isChecked();
    if(bl)
    {
        // 跳转到客户端设置
        ui->setPage->showPage(ConfigSet::Client);
        initWindow(ClientConfig);
    }
    else
    {
        // 跳转到服务器设置
        ui->setPage->showPage(ConfigSet::Server);
        initWindow(ServerConfig);
    }
}

// 登录到主窗口
void Login::on_loginBtn_clicked()
{
    // 将用户填写的登录信息从控件中读出来
    QString id = ui->netID->text();
    QString pwd = ui->netPasswd->text();

    // 判断客户端角色是否被选中了
    bool bl = ui->radioClient->isChecked();
    // 判断当前角色的配置文件是否存在, 不存在, 让用户去配置先关的信息
    // 配置文件是程序中自动生成的, 配置文件名是在函数中指定的
    JsonParse::Type t = bl ? JsonParse::Type::Client : JsonParse::Type::Server;
    bool flag = JsonParse::getInstance()->configIsExist(t);
    if(!flag)
    {
        // 配置文件不存在, 窗口跳转到设置界面, 让用户进行设置
        on_setBtn_clicked();
        return;
    }

    if(bl)
    {
        if(m_cliop == NULL)
        {
            initClientInfo();
        }
        // 发送登录的信息到 clientoperation 对象中
        emit loginInfo(id, pwd);
        // 添加任务到任务队列
        // 在登录之前需要先做秘钥的协商, 得到秘钥之后再登录 -> 任务1
        ArgInfo* arg1 = new ArgInfo;
        arg1->object = m_cliop;
        arg1->prepare = ClientOperation::consultPrepare;
        arg1->dispose = ClientOperation::consultDispose;
        TaskQueue::getInstance()->addTask(ClientOperation::communication, arg1);

        // 登录 -> 任务2
        ArgInfo* arg2 = new ArgInfo;
        arg2->object = m_cliop;
        arg2->prepare = ClientOperation::loginPrepare;
        arg2->dispose = ClientOperation::loginDispose;
        TaskQueue::getInstance()->addTask(ClientOperation::communication, arg2);
    }
    else
    {
        // 通过用户填写的登录信息, 创建数据库对象
        DBOperation db;
        // 连接数据库
        SerConfig conf = JsonParse::getInstance()->readServerConfig();
        db.connectDB(conf.dbUserName, conf.dbPasswd, conf.dbHost);
        // 查询数据库, 用户输入的ID和密码是否正确
        bl = db.userAuthentication(id, pwd);
        if(!bl)
        {
            // 失败了
            QMessageBox::warning(this, "登录失败", "您输入的ID或密码错误!!!");
            return;
        }
        // 显示服务器主界面
        ServerWindow* ser = new ServerWindow;
        ser->show();
        bool savePwd = ui->savePasswd->isChecked();
        if(savePwd)
        {
            JsonParse::getInstance()->writeLoginInfo(id, pwd, t);
        }

        // 隐藏或者关闭登录窗口
        accept();   // 隐藏模态对话框, 解除阻塞
    }
}
